package com.lcc.oa.service.activiti;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.lcc.oa.entity.*;
import com.lcc.oa.pagination.Pagination;
import com.lcc.oa.pagination.PaginationThreadUtils;
import com.lcc.oa.service.*;
import com.lcc.oa.util.BeanUtils;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricProcessInstanceQuery;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.runtime.ProcessInstanceQuery;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.spring.ProcessEngineFactoryBean;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;

/**
 * 流程相关Service
 */
@Service
public class ProcessServiceImp implements IProcessService {

    private static final Logger logger = Logger.getLogger(ProcessServiceImp.class);

    @Autowired
    protected RuntimeService runtimeService;

    @Autowired
    protected IdentityService identityService;

    @Autowired
    protected TaskService taskService;

    @Autowired
    protected RepositoryService repositoryService;

    @Autowired
    protected HistoryService historyService;

    @Autowired
    protected IUserService userService;

    @Autowired
    ProcessEngineFactoryBean processEngine;

    @Autowired
    ProcessEngineConfiguration processEngineConfiguration;

    @Autowired
    protected WorkflowService traceService;

    @Autowired
    private IVacationService vacationService;

    @Autowired
    private IExpenseService expenseService;

    @Autowired
    private ISalaryAdjustService saService;

    @Autowired
    private ISalaryService salaryService;

    /**
     * 查询代办任务
     */
    @Override
    public List<BaseVO> findTodoTask(User user, Model model){
        //taskCandidateOrAssigned查询某个人的待办任务，包含已签收、候选任务<候选人范围和候选组范围>
        TaskQuery taskQuery = this.taskService.createTaskQuery().taskCandidateOrAssigned(user.getId().toString());
        Integer totalSum = taskQuery.list().size();
        int[] pageParams = PaginationThreadUtils.setPage(totalSum);
        Pagination pagination = PaginationThreadUtils.get();
        List<Task> tasks = taskQuery.orderByTaskCreateTime().desc().listPage(pageParams[0], pageParams[1]);
        List<BaseVO> taskList = getBaseVOList(tasks);
        model.addAttribute("page", pagination.getPageStr());
        return taskList;
    }

    /**
     * 读取已结束中的流程(admin查看)
     */
    @Override
    public List<BaseVO> findFinishedProcessInstances(Model model) {
        HistoricProcessInstanceQuery historQuery = historyService.createHistoricProcessInstanceQuery().finished();
        Integer totalSum = historQuery.list().size();
        int[] pageParams = PaginationThreadUtils.setPage(totalSum);
        Pagination pagination = PaginationThreadUtils.get();
        List<HistoricProcessInstance> list = historQuery.orderByProcessInstanceEndTime().desc().listPage(pageParams[0], pageParams[1]);
        List<BaseVO> processList = new ArrayList<BaseVO>();

        for (HistoricProcessInstance historicProcessInstance : list) {
            String processInstanceId = historicProcessInstance.getId();
            List<HistoricVariableInstance> listVar = this.historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId).list();
            for(HistoricVariableInstance var : listVar){
                if("serializable".equals(var.getVariableTypeName()) && "entity".equals(var.getVariableName())){
                    BaseVO base = (BaseVO) var.getValue();
                    base.setHistoricProcessInstance(historicProcessInstance);
                    base.setProcessDefinition(getProcessDefinition(historicProcessInstance.getProcessDefinitionId()));
                    processList.add(base);
                    break;
                }
            }
        }

        model.addAttribute("page", pagination.getPageStr());
        return processList;
    }

    /**
     * 各个审批人员查看自己完成的任务
     */
    @Override
    public List<BaseVO> findFinishedTaskInstances(User user, Model model) throws Exception {
        HistoricTaskInstanceQuery historQuery = historyService.createHistoricTaskInstanceQuery()
                .taskAssignee(user.getId().toString()).finished();
        Integer totalSum = historQuery.list().size();
        int[] pageParams = PaginationThreadUtils.setPage(totalSum);
        Pagination pagination = PaginationThreadUtils.get();
        List<HistoricTaskInstance> list = historQuery.orderByHistoricTaskInstanceEndTime().desc().listPage(pageParams[0], pageParams[1]);
        List<BaseVO> taskList = new ArrayList<BaseVO>();

        for(HistoricTaskInstance historicTaskInstance : list){
            String processInstanceId = historicTaskInstance.getProcessInstanceId();
            List<HistoricVariableInstance> listVar = this.historyService
                    .createHistoricVariableInstanceQuery()
                    .processInstanceId(processInstanceId).list();
            for(HistoricVariableInstance var : listVar){
                if("serializable".equals(var.getVariableTypeName()) && "entity".equals(var.getVariableName())){
                    BaseVO base = (BaseVO) var.getValue();
                    base.setHistoricTaskInstance(historicTaskInstance);
                    base.setProcessDefinition(getProcessDefinition(historicTaskInstance.getProcessDefinitionId()));
                    taskList.add(base);
                    break;
                }
            }
        }
        model.addAttribute("page", pagination.getPageStr());
        return taskList;
    }

    /**
     * 将Task集合转为BaseVO集合
     */
    protected List<BaseVO> getBaseVOList(List<Task> tasks) {
        List<BaseVO> taskList = new ArrayList<BaseVO>();
        for (Task task : tasks) {
            String processInstanceId = task.getProcessInstanceId();
            ProcessInstance processInstance = this.runtimeService.createProcessInstanceQuery()
                    .processInstanceId(processInstanceId).active().singleResult();
            if(BeanUtils.isBlank(processInstance)){
                //如果有挂起的流程则continue
                continue;
            }
            //获取当前流程下的key为entity的variable
            BaseVO base = (BaseVO) this.runtimeService.getVariable(processInstance.getId(), "entity");
            base.setTask(task);
            base.setProcessInstance(processInstance);
            base.setProcessDefinition(getProcessDefinition(processInstance.getProcessDefinitionId()));
            taskList.add(base);
        }
        return taskList;
    }

    /**
     * 查询流程定义对象
     *
     * @param processDefinitionId 流程定义ID
     */
    protected ProcessDefinition getProcessDefinition(String processDefinitionId) {
        ProcessDefinition processDefinition = this.repositoryService
                .createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
        logger.info(processDefinition.getVersion());
        return processDefinition;
    }

    /**
     * 签收任务
     */
    @Override
    public void doClaim(User user, String taskId){
        // 用来设置启动流程的人员ID，引擎会自动把用户ID保存到activiti:initiator中
        this.identityService.setAuthenticatedUserId(user.getId().toString());
        this.taskService.claim(taskId, user.getId().toString());
    }


    /**
     * 获取评论
     */
    @Override
    public List<CommentVO> getComments(String processInstanceId) throws Exception{
        // 查询一个任务所在流程的全部评论
        List<Comment> comments = this.taskService.getProcessInstanceComments(processInstanceId);
        List<CommentVO> commnetList = new ArrayList<CommentVO>();
        for(Comment comment : comments){
            User user = this.userService.getUserById(new Integer(comment.getUserId()));
            CommentVO vo = new CommentVO();
            vo.setContent(comment.getFullMessage());
            vo.setTime(comment.getTime());
            vo.setUserName(user.getName());
            commnetList.add(vo);
        }
        return commnetList;
    }


    /**
     * 显示流程图,带流程跟踪
     */
    @Override
    public InputStream getDiagram(String processInstanceId){
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId).singleResult();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        List<String> activeActivityIds = runtimeService.getActiveActivityIds(processInstanceId);
        // 不使用spring请使用下面的两行代码
//    	ProcessEngineImpl defaultProcessEngine = (ProcessEngineImpl) ProcessEngines.getDefaultProcessEngine();
//    	Context.setProcessEngineConfiguration(defaultProcessEngine.getProcessEngineConfiguration());

        // 使用spring注入引擎请使用下面的这行代码
        processEngineConfiguration = processEngine.getProcessEngineConfiguration();
        Context.setProcessEngineConfiguration((ProcessEngineConfigurationImpl) processEngineConfiguration);

        //通过引擎生成png图片，并标记当前节点,并把当前节点用红色边框标记出来，弊端和直接部署流程文件生成的图片问题一样-乱码！。
        ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
        InputStream imageStream = diagramGenerator.generateDiagram(bpmnModel, "png", activeActivityIds);
        return imageStream;
    }

    /**
     * 显示图片-通过流程ID，，不带流程跟踪(没有乱码问题)
     */
    @Override
    public InputStream getDiagramByProInstanceId_noTrace(String resourceType, String processInstanceId){

        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId).singleResult();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(processInstance.getProcessDefinitionId())
                .singleResult();

        String resourceName = "";
        if (resourceType.equals("png") || resourceType.equals("image")) {
            resourceName = processDefinition.getDiagramResourceName();
        } else if (resourceType.equals("xml")) {
            resourceName = processDefinition.getResourceName();
        }
        InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName);
        return resourceAsStream;
    }

    /**
     * 显示图片-通过部署ID，不带流程跟踪(没有乱码啊问题)
     */
    @Override
    public InputStream getDiagramByProDefinitionId_noTrace(String resourceType,
                                                           String processDefinitionId) throws Exception {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
        String resourceName = "";
        if (resourceType.equals("png") || resourceType.equals("image")) {
            resourceName = processDefinition.getDiagramResourceName();
        } else if (resourceType.equals("xml")) {
            resourceName = processDefinition.getResourceName();
        }
        InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName);
        return resourceAsStream;
    }

    /**
     * 查看正在运行的请假流程
     */
    @Override
    public List<BaseVO> listRuningVacation(User user) throws Exception{
        List<Vacation> listVacation = this.vacationService.findByStatus(user.getId(), BaseVO.PENDING);
        List<BaseVO> result = new ArrayList<BaseVO>();
        if(listVacation != null ){
            for (Vacation vac : listVacation) {
                if(vac.getProcessInstanceId() == null){
                    continue;
                }
                // 查询流程实例
                ProcessInstance pi = this.runtimeService
                        .createProcessInstanceQuery()
                        .processInstanceId(vac.getProcessInstanceId())
                        .singleResult();
                Task task = this.taskService.createTaskQuery().processInstanceId(vac.getProcessInstanceId()).singleResult();
                if (pi != null) {
                    // 查询流程参数
                    BaseVO base = (BaseVO) this.runtimeService.getVariable(pi.getId(), "entity");
                    base.setTask(task);
                    base.setProcessInstance(pi);
                    base.setProcessDefinition(getProcessDefinition(pi.getProcessDefinitionId()));

                    result.add(base);
                }
            }
        }
        return result;
    }

    /**
     * 查看正在运行的报销流程
     */
    @Override
    public List<BaseVO> listRuningExpense(User user) throws Exception{
        List<ExpenseAccount> listVacation = this.expenseService.findByStatus(user.getId(), BaseVO.PENDING);
        List<BaseVO> result = new ArrayList<BaseVO>();
        if(listVacation != null ){
            for (ExpenseAccount expense : listVacation) {
                if(expense.getProcessInstanceId() == null){
                    continue;
                }
                // 查询流程实例
                ProcessInstance pi = this.runtimeService
                        .createProcessInstanceQuery()
                        .processInstanceId(expense.getProcessInstanceId())
                        .singleResult();
                Task task = this.taskService.createTaskQuery().processInstanceId(expense.getProcessInstanceId()).singleResult();
                if (pi != null) {
                    // 查询流程参数
                    BaseVO base = (BaseVO) this.runtimeService.getVariable(pi.getId(), "entity");
                    base.setTask(task);
                    base.setProcessInstance(pi);
                    base.setProcessDefinition(getProcessDefinition(pi.getProcessDefinitionId()));

                    result.add(base);
                }
            }
        }
        return result;
    }

    /**
     * 查看正在运行的薪资调整流程
     */
    @Override
    public List<BaseVO> listRuningSalaryAdjust(User user) throws Exception {
        List<SalaryAdjust> listSalary = this.saService.findByStatus(user.getId(), BaseVO.PENDING);
        List<BaseVO> result = new ArrayList<BaseVO>();
        if(listSalary != null ){
            for(SalaryAdjust sa : listSalary){
                if(sa.getProcessInstanceId() == null){
                    continue;
                }
                // 查询流程实例
                ProcessInstance pi = this.runtimeService
                        .createProcessInstanceQuery()
                        .processInstanceId(sa.getProcessInstanceId())
                        .singleResult();
                Task task = this.taskService.createTaskQuery().processInstanceId(sa.getProcessInstanceId()).singleResult();
                if (pi != null) {
                    // 查询流程参数
                    BaseVO base = (BaseVO) this.runtimeService.getVariable(pi.getId(), "entity");
                    base.setTask(task);
                    base.setProcessInstance(pi);
                    base.setProcessDefinition(getProcessDefinition(pi.getProcessDefinitionId()));
                    result.add(base);
                }
            }
        }
        return result;
    }

    @Override
    public String startSalaryAdjust(SalaryAdjust salary) throws Exception {
        // 用来设置启动流程的人员ID，引擎会自动把用户ID保存到activiti:initiator中
        this.identityService.setAuthenticatedUserId(salary.getUserId().toString());
        Salary sa = this.salaryService.findByUserId(salary.getUserId().toString());
        Map<String, Object> variables = new HashMap<String, Object>();
        variables.put("entity", salary);
        // 此处已经加入监听来动态分配任务--UserTaskLintener
        // 总监组审批
        // variables.put("auditGroup", "director");
        variables.put("businessKey", salary.getId());
        // 原有薪金(回滚用)
        variables.put("baseMoney", sa.getBaseMoney());
        String businessKey = salary.getBusinessKey();
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("com.zml.oa.salary", businessKey, variables);
        String processInstanceId = processInstance.getId();
        salary.setProcessInstanceId(processInstanceId);
        this.saService.doUpdate(salary);
        logger.info("processInstanceId: "+processInstanceId);
        // 最后要设置null，就是这么做，还没研究为什么
        this.identityService.setAuthenticatedUserId(null);
        return processInstanceId;
    }

    @Override
    public String startVacation(Vacation vacation) throws Exception {
        // 用来设置启动流程的人员ID，引擎会自动把用户ID保存到activiti:initiator中
        identityService.setAuthenticatedUserId(vacation.getUserId().toString());
        Map<String, Object> variables = new HashMap<String, Object>();
        variables.put("entity", vacation);
        //由userTask自动分配审批权限
        //if(vacation.getDays() <= 3){
        //	variables.put("auditGroup", "manager");
        //}else{
        //	variables.put("auditGroup", "director");
        //}
        String businessKey = vacation.getBusinessKey();
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("com.zml.oa.vacation", businessKey, variables);
        String processInstanceId = processInstance.getId();
        vacation.setProcessInstanceId(processInstanceId);
        this.vacationService.doUpdate(vacation);

        logger.info("processInstanceId: "+processInstanceId);
        //最后要设置null，就是这么做，还没研究为什么
        this.identityService.setAuthenticatedUserId(null);
        return processInstanceId;
    }

    @Override
    public String startExpense(ExpenseAccount expense) throws Exception {
        // 用来设置启动流程的人员ID，引擎会自动把用户ID保存到activiti:initiator中
        this.identityService.setAuthenticatedUserId(expense.getUserId().toString());
        Map<String, Object> variables = new HashMap<String, Object>();
        variables.put("entity", expense);
        //此处已经加入监听来动态分配任务--UserTaskLintener
        //variables.put("auditGroup", "finance");	//财务组审批
        String businessKey = expense.getBusinessKey();
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("com.zml.oa.expense", businessKey, variables);
        String processInstanceId = processInstance.getId();
        expense.setProcessInstanceId(processInstanceId);
        this.expenseService.doUpdate(expense);

        logger.info("processInstanceId: "+processInstanceId);
        //最后要设置null，就是这么做，还没研究为什么
        this.identityService.setAuthenticatedUserId(null);
        return processInstanceId;
    }

    @Override
    public void complete(String taskId, String content, String userid, Map<String, Object> variables) {
        Task task = this.taskService.createTaskQuery().taskId(taskId).singleResult();
        // 根据任务查询流程实例
        String processInstanceId = task.getProcessInstanceId();
        ProcessInstance pi = this.runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        //评论人的id  一定要写，不然查看的时候会报错，没有用户
        this.identityService.setAuthenticatedUserId(userid);
        // 添加评论
        if(content != null){
            this.taskService.addComment(taskId, pi.getId(), content);
        }
        // 完成任务
        this.taskService.complete(taskId, variables);
    }

    @Override
    public List<ProcessInstance> listRuningProcess(Model model) throws Exception {
        ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery();
        Integer totalSum = processInstanceQuery.list().size();
        int[] pageParams = PaginationThreadUtils.setPage(totalSum);
        Pagination pagination = PaginationThreadUtils.get();
        List<ProcessInstance> list = processInstanceQuery.orderByProcessInstanceId().desc().listPage(pageParams[0], pageParams[1]);
        model.addAttribute("page", pagination.getPageStr());
        return list;
    }

    @Override
    public void activateProcessInstance(String processInstanceId) throws Exception {
        runtimeService.activateProcessInstanceById(processInstanceId);
    }

    @Override
    public void suspendProcessInstance(String processInstanceId) throws Exception {
        runtimeService.suspendProcessInstanceById(processInstanceId);
    }

}
